package com.agentlab.imitation.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * ToString类用于将一个被封装的JAVA对象转换成字符串，并且只会输出被封装的JAVA对象的具有公有的访问方法的属性的值，如果属性值等于null，则会将null值转换成字符串“&lt;null&gt;”。
 * 另外还对hibernate的代理对象进行了处理，如果代理对象没有初始化，则会被转换成字符串“&lt;uninitialized&gt;”。
 * 
 * @author 
 * 
 */
public class ToString {
	/* 被转换成字符串的java对象 */
	private Object object;

	/* java对象的所有的属性(包括继承的) */
	private List<Field> fields = new ArrayList<Field>();

	/* java对象的所有的、公有的访问方法 */
	private Map<String, Method> methods = new HashMap<String, Method>();

	/* true表示输出字符串的格式为多行，false为单行 */
	private boolean multiLineStyle = false;

	/* 此属性不为null表示程序运行在一个hibernate环境之中 */
	private static Class hibernate = null;

	/* 输出时将要被忽略的属性 */
	private Set<String> ignoreFiledNames = new HashSet<String>();

	static {
		try {
			hibernate = Class.forName("org.hibernate.Hibernate");
		} catch (ClassNotFoundException e) {
		}
	}

	/**
	 * 构建一个新的ToString对象
	 * 
	 * @param object
	 *            要被转换的java对象
	 */
	public ToString(Object object) {
		super();
		this.object = object;
		if(this.object != null){
			this.initFieldNames(this.object.getClass());
			this.initMethod(this.object.getClass());
		}
	}

	/**
	 * 设置输出字符串格式为多行
	 * 
	 * @return 返回当前的ToString对象本身
	 */
	public ToString setMultiLineStyle() {
		multiLineStyle = true;
		return this;
	}

	/*
	 * 获得此类定义的所有属性(包括继承的属性)
	 */
	private void initFieldNames(Class clazz) {
		Field[] fields = clazz.getDeclaredFields();
		for (Field f : fields) {
			this.fields.add(f);
		}

		Class superClass = clazz.getSuperclass();// 返回父类的Class类的实例
		if (!superClass.getName().equals("java.lang.Object")) {
			this.initFieldNames(superClass);// 递归查找继承的属性
		}
	}

	/*
	 * 获得此类定义的所有公共的访问方法
	 */
	private void initMethod(Class clazz) {
		// 返回Class 对象所表示的类或接口（包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口的公共 member 方法
		Method[] methods = clazz.getMethods();
		for (Method m : methods) {
			if (this.isGetMethod(m)) {
				this.methods.put(m.getName(), m);
			}
		}
	}

	/*
	 * 判断是不是一个访问方法(方法名以get或is开头、无参、返回值的类型不能是void)
	 */
	private boolean isGetMethod(Method m) {
		if (0 != m.getParameterTypes().length) {
			return false;
		}
		if (m.getReturnType().getName().equals("void")) {
			return false;
		}
		String methodName = m.getName();
		if (0 == methodName.indexOf("get") || 0 == methodName.indexOf("is")) {
			return true;
		} else {
			return false;
		}
	}

	/*
	 * 获得属性对应的访问方法的名称
	 */
	private String getFieldMappingMethodName(Field f) {
		String methodName = null;

		char[] chars = f.getName().toCharArray();// 将属性首字母改成大写
		String fieldName = "";
		for (int i = 0; i < chars.length; i++) {
			if (0 == i) {
				fieldName = (fieldName + chars[i]).toUpperCase();
			} else {
				fieldName = fieldName + chars[i];
			}
		}
		if (f.getType().isPrimitive()
				&& f.getType().getName().equals("boolean")) {// boolean类型的访问方法以is开头，其它都是以get开头
			methodName = "is" + fieldName;
		} else {
			methodName = "get" + fieldName;
		}
		return methodName;
	}

	/*
	 * 使用属性的访问方法获得属性值
	 */
	private Object getValue(Method m) {
		Object value = null;
		try {
			value = m.invoke(this.object);
			if (null == value) {
				value = "<null>";
			} else if (null != hibernate && !isInitialized(value)) {
				value = "<uninitialized>";
			} else if (value.getClass().isArray()) {
				Object[] arr = (Object[]) value;
				List<Object> list = new ArrayList<Object>();
				for (Object o : arr) {
					list.add(o);
				}
				value = list;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return value;
	}

	/*
	 * 如果value是一个hibernate的代理对象、并且也没有初始化则返回false，否则返回true
	 */
	private boolean isInitialized(Object value) {
		try {
			Method m = hibernate.getMethod("isInitialized", Object.class);
			Boolean b = (Boolean) m.invoke(null, value);
			return b.booleanValue();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 设置在输出JAVA对象时要忽略的属性
	 * 
	 * @param fieldNames
	 *            要被忽略的属性
	 * @return 返回当前的ToString对象本身
	 */
	public ToString ignore(String... fieldNames) {
		if (null != fieldNames && 0 != fieldNames.length) {
			for (String s : fieldNames) {
				this.ignoreFiledNames.add(s);
			}
		}
		return this;
	}

	/**
	 * 返回一个字符串，表示被当前的ToString对象封装的JAVA对象
	 */
	public String toString() {
		if(this.object == null)return "null";
		StringBuffer bf = new StringBuffer();
		bf.append(this.object.getClass().getName()).append("[");
		String methodName = null;
		Object value = null;
		Field f = null;
		int j = 0;
		for (int i = 0; i < this.fields.size(); i++) {
			f = this.fields.get(i);
			if (this.ignoreFiledNames.contains(f.getName())) {
				continue;// 根据集合ignoreFiledNames当中的值，忽略某些属性
			}
			methodName = this.getFieldMappingMethodName(f);
			if (this.methods.containsKey(methodName)) {
				if (0 != j) {
					bf.append(", ");
				}
				if (multiLineStyle) {
					bf.append("\n\t");
				}
				value = this.getValue(this.methods.get(methodName));
				bf.append(f.getName()).append("=").append(value);
				j++;
			}
		}
		if (multiLineStyle) {
			bf.append("\n]");
		} else {
			bf.append("]");
		}
		return bf.toString();
	}
}
